using System.Collections;
using System.Threading;
using Microsoft.SPOT.Hardware;

namespace Melody_Glider.MelodyContent
{
    public class Melody
    {
        private int _bpm;
        private int _baseOctave;
        private readonly ArrayList _notesArray;
        
        public Melody()
        {
            _notesArray = new ArrayList();
            _bpm = new int();
        }

        public Melody(int bpm, int baseOctave)
        {
            _bpm = bpm;
            _baseOctave = baseOctave;
            _notesArray = new ArrayList();
        }

        public int GetBpm()
        {
            return _bpm;
        }

        public void SetBpm(int bpm)
        {
            _bpm = bpm;
        }

        public int GetBaseOctave()
        {
            return _baseOctave;
        }

        public void SetBaseOctave(int baseOctave)
        {
            _baseOctave = baseOctave;
        }

        public Note GetNote(int idx)
        {
            return (Note)_notesArray[idx];
        }

        public ArrayList GetMelody()
        {
            return _notesArray;
        }
        
        public void AddNote(Note note)
        {
            _notesArray.Add(note);
        }

        public void InsertNote(Note note, int idx)
        {
            _notesArray.Insert(idx, note);
        }

        public void CloneNote(int idx)
        {
            _notesArray.Insert(idx + 1, new Note((Note)_notesArray[idx]));
        }

        public void ReplaceNote(Note note, int idx)
        {
            _notesArray.RemoveAt(idx);
            _notesArray.Insert(idx, note);
        }

        public void RemoveNote(int idx)
        {
            _notesArray.RemoveAt(idx);
        }
        
        public void ClearMelody()
        {
            _notesArray.Clear();
        }

        public void PlayMelody()
        {
            foreach (Note note in _notesArray)
            {
                PlayNote(note);
            }
        }

        public void PlayNote(int idx)
        {
            PlayNote((Note)_notesArray[idx]);
        }

        private void PlayNote(Note note)
        {
            var howLong = (note.GetDuration() / ((double)_bpm / 60)) * 1000; //i.eg. tempo 60bpm, half note -> (2/(60/60)) = 2s = 2000ms
                                                                             //metrum 4/4;
            if (note.IsStaccato())
                howLong = ((note.GetDuration() / 2) / ((double)_bpm / 60)) * 1000;
            if (note.IsDotted())
                howLong = ((note.GetDuration() * 1.5) / ((double)_bpm / 60)) * 1000;

            var howHigh = note.GetPitch().Symbol;
            for (var i = 0; i < _baseOctave+note.GetPitch().OctaveShift; i++) //shifting to the desired octave
                howHigh *= 2;

            Utility.Piezo((uint)howHigh, (uint)howLong);

            if (note.IsLigature())
                return;
            if (note.IsStaccato())
                //bug?: Thread.Sleep worked if there were two staccato notes, but with one non-staccato and two staccato, these staccato were played without pause :?
                //Thread.Sleep((int)howLong);
                PlayNote(new Note(new Pitch(Pitch.R, 0), note.GetDuration()/2, false, false, false));
            Thread.Sleep((int)(howLong / 5)); //arbitrary somehow should be bpm-dependent, no note-dependent
        }
        
        public Melody Parse(string melody)
        {
            return null; //todo
        }

        public string ToString(Melody melody)
        {
            return null; //todo
        }
    }
}
